package com.olap.starter.controller.admin;


import com.olap.starter.common.service.CRUDServices;
import com.olap.starter.common.service.online.OnlineServices;
import com.olap.starter.common.util.DBType;
import com.olap.starter.common.util.QueryGeneratorUtil;
import com.olap.starter.common.util.SqlUtils;
import com.olap.starter.common.vo.*;
import com.olap.starter.config.dataSource.JdbcTemplateSource;
import com.olap.starter.core.response.Result;
import com.olap.starter.util.StringUtils;
import com.olap.starter.util.sql.jsqlparser.ColMappingDto;
import com.olap.starter.util.sql.jsqlparser.JsqlParserUtil;
import com.olap.starter.util.sql.jsqlparser.NormalSqlStructureDto;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.olap.starter.common.util.QueryGeneratorUtil.page;
import static com.olap.starter.core.response.ResultUtil.success;

@RestController
@Log4j2
@RequestMapping("/online")
public class OnlineController {

    @Autowired
    private OnlineServices onlineService;

    @Autowired
    private CRUDServices crudServices;

    @Autowired
    private JdbcTemplateSource jdbcTemplateSource;

    @GetMapping("/initAll")
    public Result init(long uuid) {
        return success(onlineService.init(uuid));
    }

    @GetMapping("/init")
    public Result init(long uuid, @RequestParam(defaultValue = "t1", name = "theme") String theme) {
        return success(onlineService.init(uuid, theme));
    }

    @GetMapping("/refreshCache")
    public Result refreshCache() {
        return onlineService.refreshCache();
    }

    @PostMapping("/dictTable")
    public List<Map<String, Object>> dictTable(@RequestBody DictTableVO dictTableVO) {
        String sql = dictTableVO.getSQL(jdbcTemplateSource);
        log.info("执行SQL：" + sql);
        List<Map<String, Object>> list = crudServices.doGetDataList(sql, dictTableVO.getDbName());
        for (Map<String, Object> map : list) {
            map.put("label", map.get("dictLabel"));
            map.put("value", map.get("dictValue"));
        }
        return list;
    }

    @PostMapping("/dictTag")
    public List<Map<String, Object>> dictTag(@RequestBody DictTagVO dictTableVO) {
        QueryParameter queryParameter = dictTableVO.getQueryParameter();
        JdbcTemplate jdbcTemplate = jdbcTemplateSource.getJdbcTemplate(dictTableVO.getDbName());
        queryParameter.setDbType(DBType.getType(jdbcTemplate));
        ExecuteQuery executeQuery = QueryGeneratorUtil.initQuery(queryParameter);
        String exSql = QueryGeneratorUtil.replacePlaceholdersWithQuestionMarks(executeQuery.getExSql());
        Object[] sqlAndParams = QueryGeneratorUtil.getSqlAndParams(executeQuery);
        log.info("执行dictTag：" + executeQuery.getExSql());
        return jdbcTemplate.queryForList(exSql, sqlAndParams);
    }


    @GetMapping("/getDBTableList")
    public Result getDBTableList(String dbName) {
        return success(crudServices.doGetDataList("", dbName));
    }

    @GetMapping("/getDBTableColumnList/{dbName}")
    public Result getDBTableColumnList(@PathVariable("dbName") String dbName, String tableName) {
        return success(crudServices.doGetDataList("", dbName, tableName));
    }

    @PostMapping("/getTableFiled")
    public Result getTableFiled(@RequestBody Map<String, String> enty) {
        String dbName = enty.get("dbName");
        String sql = enty.get("sql");
        String definitionSql = enty.get("definitionSql");
        List<Map<String, Object>> mapList = new ArrayList<>();
        try {
            NormalSqlStructureDto normalSqlStructureDto = JsqlParserUtil.getStructure(sql);
            List<String> tableNames = normalSqlStructureDto.getTableNames();
            Map<String, Map<String, Map<String, Object>>> stringListMap = doGetTableFiled(tableNames, dbName);
            for (ColMappingDto colMapping : normalSqlStructureDto.getColMappings()) {
                if ("column".equals(colMapping.getFunctionName().toLowerCase())) {
                    String tableName = normalSqlStructureDto.getTableThemeMappings().get(colMapping.getTable());
                    if (tableName == null) {
                        tableName = colMapping.getTable();
                    }
                    Map<String, Object> map = stringListMap.get(tableName).get(colMapping.getName());
                    if (map == null) {
                        throw new RuntimeException("数据库[" + dbName + "]不存在表[" + tableName + "]字段[" + colMapping.getName() + "]，请检查SQL是否正常!");
                    }
                    map.put("prop", colMapping.getAlias());
                    mapList.add(map);
                } else {
                    Map<String, Object> map = getDefault(colMapping.getName());
                    map.put("prop", colMapping.getAlias());
                    mapList.add(map);
                }
            }
            return success(mapList);
        } catch (Exception e) {
            if ("1".equals(definitionSql)) {
                QueryParameter queryParameter = new QueryParameter();
                queryParameter.setDbName(dbName);
                queryParameter.setSql(sql);
                queryParameter.setEnablePage(true);
                queryParameter.setPageSize(1);
                JdbcTemplate jdbcTemplate = jdbcTemplateSource.getJdbcTemplate(queryParameter.getDbName());
                queryParameter.setDbType(SqlUtils.getDbType(jdbcTemplate));
                String exSql = page(queryParameter, sql);
                List<Map<String, Object>> maps = crudServices.doGetDataList(exSql, jdbcTemplate);
                List<Map<String, Object>> respMap = new ArrayList<>();
                for (Map<String, Object> map : maps) {
                    int i = 1;
                    for (String key : map.keySet()) {
                        Map<String, Object> val = new HashMap<>();
                        val.put("table_name", "");
                        val.put("column_name", key);
                        val.put("visible", 1);
                        val.put("sort", i);
                        val.put("column_comment", key);
                        val.put("is_increment", 0);
                        val.put("checked", 1);
                        val.put("$cellEdit", true);
                        val.put("prop", key);
                        i++;
                        respMap.add(val);
                    }
                }
                return success(respMap);
            } else {
                throw new RuntimeException(e);
            }
        }

    }

    @PostMapping("/getTableSetFiled")
    public Result getTableSetFiled(@RequestBody Map<String, Object> map) {
        String dbName = map.get("dbName").toString();
        String sql = map.get("sql").toString();
        String definitionSql = map.get("definitionSql").toString();
        try {
            NormalSqlStructureDto normalSqlStructureDto = JsqlParserUtil.getStructure(sql);
            List<String> tableNames = normalSqlStructureDto.getTableNames();
            List<ColMappingDto> colMappings = normalSqlStructureDto.getColMappings();
            Map<String, Map<String, Map<String, Object>>> stringListMap = doGetTableFiled(tableNames, dbName);
            Map<String, String> tableThemeMappings = normalSqlStructureDto.getTableThemeMappings();
            for (String tableName : stringListMap.keySet()) {
                Map<String, Map<String, Object>> value = stringListMap.get(tableName);

                for (String key : value.keySet()) {
                    for (ColMappingDto colMappingDto : colMappings) {
                        if(!colMappingDto.getAlias().equals(key) && !colMappingDto.getName().equals(key)) {
                            continue;
                        }
                        boolean put = StringUtils.isEmpty(colMappingDto.getTable()) || StringUtils.isEmpty(
                                tableThemeMappings.get(colMappingDto.getTable())) ||
                                tableThemeMappings.get(colMappingDto.getTable()).equals(tableName);
                        if (put) {
                            value.get(key).put("select", "1");
                        }
                    }
                }
            }
            return success(stringListMap);
        } catch (Exception e) {
            if ("1".equals(definitionSql)) {
                QueryParameter queryParameter = new QueryParameter();
                queryParameter.setDbName(dbName);
                queryParameter.setSql(sql);
                queryParameter.setEnablePage(true);
                queryParameter.setPageSize(1);
                QueryData queryData = crudServices.doGetData(queryParameter);
                return success(queryData);
            } else {
                throw new RuntimeException(e);
            }
        }

    }

    private Map<String, Object> getDefault(String columnName) {
        Map<String, Object> map = new HashMap<>();
        map.put("column_name", columnName);
        map.put("is_pk", 0);
        map.put("visible", 1);
        map.put("is_increment", 0);
        map.put("checked", 1);
        return map;
    }

    private Map<String, Map<String, Map<String, Object>>> doGetTableFiled(List<String> tableNames, String dbName) {
        Map<String, Map<String, Map<String, Object>>> map = new HashMap<>();
        for (String t : tableNames) {
            List<Map<String, Object>> mapList = crudServices.doGetDataList("", dbName, t);
            Map<String, Map<String, Object>> filedMap = new HashMap<>();
            for (Map<String, Object> columnMap : mapList) {
                columnMap.put("$cellEdit", true);
                filedMap.put(columnMap.get("column_name").toString(), columnMap);
            }
            map.put(t, filedMap);
        }
        return map;
    }

    @PostMapping("/refreshDataSource")
    public Result refreshDataSource() {
        log.info("Start refreshing data source");
        jdbcTemplateSource.refreshDataSource();
        log.info("Finish refreshing data source");
        return success();
    }
}
